ゆめみ Flutter 研修で学んだことのメモ (課題クリアに必要な知識以外)

研修リポジトリ: GitHub - daichikuwa0618/flutter-weather-app: Dummy Weather App Built with Flutter にて学んだことのメモ。
ただし、課題をクリアするのに必要な知識は除く。 (例えば Session/4 で求められている Mixin とは何かみたいな話は含んでいない)

XxxPageXxxScreen の命名について

https://github.com/daichikuwa0618/flutter-weather-app/pull/14#discussion_r1602679988

画面 Widget の接尾辞の流派は主に Page と Screen があります。Screen だと WeatherScreen ですね。
なぜ Screen を好む流派がいるかというと、Flutter には Page クラスが存在して、それは Widget ではなくルーティングに関係するクラスなので、WeatherPage だとルーティングに関係するように撮られる可能性があるからです。

Callable Object について

https://github.com/daichikuwa0618/flutter-weather-app/pull/15#discussion_r1602534343

Swift でいうところの callAsFunction を Dart でも利用する にまとめた。

Future.unawaitedFuture.microtask って何が違う?

https://github.com/daichikuwa0618/flutter-weather-app/pull/16#discussion_r1604448233

The Event Loop and Dart (翻訳) #Dart - Qiita を見るのが一番良かった。

Future.microtask & addPostFrameCallback その違い も良かった気がするけど、忘れちゃった。時間があったら見よう。

Extension Type の利用

https://github.com/daichikuwa0618/flutter-weather-app/pull/19#discussion_r1609187243

extension type _Request(({String area, DateTime dateTime}) value) {
  Map<String, String> toJson() {
    return {
      'area': value.area,
      'date': value.dateTime.toString(),
    };
  }
}

Dart の Extension type

一見 record 型を typedef すればいいんじゃ?と思うかも知れないが、メソッドや getter を生やすときに違いが出てくる。
↓ は雰囲気で書いたから違うところあるかも。

// typedef 板
typedef MyInt = int;

Extension on MyInt {
  MyInt double() {
    return this * 2;
  }
}

MyInt myInt = 1;
myInt.double();
int pureInt = 1;
pureInt.double(); // できちゃう

extension type MyInt(int value) {
  MyInt double() {
    return this * 2;
  }
}

var myInt = MyInt(1);
myInt.double();
int pureInt = 1;
// pureInt.double(); // MyInt として静的に型付けされてないとできない

生成コストの無い独自型って感じで、自前の型を作りすぎないでプリミティブな型で表現する データ指向プログラミング と相性が良さそう (多分)。

toJson メソッドのみを生成させる方法

https://github.com/daichikuwa0618/flutter-weather-app/pull/20#discussion_r1611052469 このコメント時点では良く分かってなかった。
後で分かったのでまとめた: freezed で toJson だけ生成させる方法

ローカル状態は Riverpod で提供しない

DO/DON'T | Riverpod

Providers are designed to be for shared business state. They are not meant to be used for local widget state, such as for:

If you are looking for a way to handle local widget state, consider using flutter_hooks instead.
One reason why this is discouraged is that such state is often scoped to a route.
Failing to do so could break your app's back button, due to a new page overriding the state of a previous page.

Provider の設計は難しいということ

テストのことを考えると family Provider を作るよりも Function を返す Provider の方が書き味がいい みたいな話もあるが、その場合は他の画面から呼び出したときに値がキャッシュされない (ですよね?)
XxxRepository みたいに何かしらのオブジェクトを返す Provider を作っても ref.xxxProvider.getXxx() を他の場所から呼ぶとキャッシュされた値が使われるのではなく、改めてメソッドが実行される (ですよね?)

となると、キャッシュ戦略に寄っては Family Provider も使い所次第で積極的に使っていけば、同じ引数のものは不要な API 通信や DB 操作が無くせるというのは結構大きなメリットに感じる。
特に、SSOT を守りやすくなりそう。 (更新をかけたいタイミングで invalidate すれば全部の画面でリフレッシュされるので)

ここはテストコード丸々が結構面白いんじゃないかと思っている。
flutter-weather-app/test/weather/weather_notifier_test.dart

やりたいこととしては、Notifier が依存している Use Case の結果が変わったときに、期待する状態になっていること。単独の結果ではなく、失敗 → 成功等の前回の結果に依存するテストを書きたい。